

def readPgkData(serial):
    res =0 
    resBytes = []
    try:
        recHeaderBytes = serial.read(1)
        if len(recHeaderBytes)>0:
            recHeader = recHeaderBytes[0]
            if recHeader==0xE5:
                res = 0xE5
            elif recHeader==0x10:
                count = serial.inWaiting()
                if count>= 4:
                    recBytes = serial.read(4)
                    if( len(recBytes) >=4 and recBytes[3]==0x16):
                        resBytes.append(0x10)
                        for i in recBytes:
                            resBytes.append(i)
                        res = 0x10
            elif recHeader == 0x68 :
                res = 0x68
                rec68HeadBytes = serial.read(3)
                flag = len(rec68HeadBytes)>=3 and rec68HeadBytes[0]==rec68HeadBytes[1] and rec68HeadBytes[2]==0x68
                if flag==True:
                    pgkSize = rec68HeadBytes[0]+2
                    rec68Bytes = serial.read(pgkSize)
                    if( len(rec68Bytes) >=pgkSize and rec68Bytes[pgkSize-1]==0x16):
                        resBytes.append(0x68)
                        for i in rec68Bytes:
                            resBytes.append(i)
                        res = 0x68
            else:
                res = -1
    except er:
        print(er)
    return res , resBytes


class TIce101Master :
    cmdBytes_0 = bytes([0x10 , 0x49 , 0x01 , 0x4A ,0x16 ])
    cmdBytes_1 = bytes([0x10 , 0x40 , 0x01 , 0x41 , 0x16 ])
    cmdBytes_2 = bytes([0x68 , 0x09 , 0x09 , 0x68 , 0x73 , 0x01 , 0x64 , 0x01 , 0x06 , 0x01 , 0x00 , 0x00 , 0x14 , 0xF4 , 0x16 ])
    cmdByte_31 = bytes([0x10 , 0x7B , 0x01 , 0x7C , 0x16 ])
    cmdByte_callLevel1_0 = bytes([0x10 , 0x5A , 0x01 , 0x5B , 0x16 ])
    cmdByte_callLevel1_1 = bytes([0x10 , 0x7A , 0x01 , 0x7B , 0x16 ])
    cmdByte_callLevel2_0 = bytes([0x10 , 0x5B , 0x01 , 0x5C , 0x16 ])
    cmdByte_callLevel2_1 = bytes([0x10 , 0x7B , 0x01 , 0x7C , 0x16 ])

    def __init__(self) -> None:
        self.serial = None
        self.com = "COM3"
        self.comRate = 2400
        self.callLevelFlag1 = 0
        self.time_level1=0
        self.wait_level1 = 2
        self.levelFlag1 = False
        self.callLevelFlag2 = 0
        self.time_level2=0
        self.wait_level2 = 2
        self.levelFlag2 = False

        self.isActived = False

        self.onRecDataEvent = None
    def start(self):
        try:
            openCommFlag = self.openCom()
            if openCommFlag :
                self.requestLink()
                self.resetLink()
                self.callAllData()

                self.isActived = True
                self.levelFlag1 = True
                self.levelFlag2 = True
                threadRecPgk = Thread(target=self.recPgk)
                threadRecPgk.setDaemon(True)
                threadRecPgk.start()


                threadSendCmd = Thread(target=self.requestDataLevel)
                threadSendCmd.setDaemon(True)
                threadSendCmd.start()

                  
                while self.isActived :
                    inKey = input("please input command:")
                    if inKey == "exit":
                        self.isActived = False
                print("exit by input words:'exit'")
                

        except er:
            print(er)
    def openCom(self):
        res = 0 
        try:
            self.serial = serial.Serial(self.com, self.comRate  )
            isOpen = self.serial.isOpen()
            if isOpen:
                res = 1
                print("open com:"+ self.com+" success.")
            else:
                print("open com:"+ self.com+" faild.")
        except er:
            print(er)
        return res
    def requestLink(self):
        res = 0 
        try:
            res = self.serial.write(TIce101Master.cmdBytes_0)
            self.logSend(TIce101Master.cmdBytes_0)
            recFlag , recBytes = self.readPgkData() 
            self.logRec(recBytes)
        except er:
            print(er)
        return res
    def resetLink(self):
        res = 0 
        try:
            res= self.serial.write(TIce101Master.cmdBytes_1)
            self.logSend(TIce101Master.cmdBytes_1)
            recFlag , recBytes = self.readPgkData()
            self.logRec(recBytes) 
        except er:
            print(er)
        return res
    def callAllData(self):
        res = 0 
        try:
            res = self.serial.write(TIce101Master.cmdBytes_2)
            self.logSend(TIce101Master.cmdBytes_2)
            recFlag , recBytes = self.readPgkData()
            self.logRec(recBytes)
        except er:
            print(er)
        return res
    def requestDataLevel1(self):
        res = 0 
        try:            
            if self.callLevelFlag1==0 :
                self.callLevelFlag1 = 1
                res = self.serial.write( TIce101Master.cmdByte_callLevel1_0)
                self.logSend(TIce101Master.cmdByte_callLevel1_0)
            else:
                self.callLevelFlag1 = 0
                res = self.serial.write( TIce101Master.cmdByte_callLevel1_1)
                self.logSend(TIce101Master.cmdByte_callLevel1_1)
        except er:
            print(er)
        return res
    def requestDataLevel2(self):
        res = 0 
        try: 
            if self.callLevelFlag2==0 :
                self.callLevelFlag2 = 1
                res = self.serial.write( self.cmdByte_callLevel2_0)
                self.logSend(self.cmdByte_callLevel2_0)
            else:
                self.callLevelFlag2 = 0
                res = self.serial.write( self.cmdByte_callLevel2_1)
                self.logSend(self.cmdByte_callLevel2_1)
        except er:
            print(er)
        return res
    def requestDataLevel(self):
        res = 0 
        try:
            while 1>0:
                try:
                    time.sleep(0.1)
                    nowTm = time.time()
                    dltLevel1 = nowTm - self.time_level1
                    if dltLevel1 >= self.wait_level1  and self.levelFlag1:
                        self.time_level1 = nowTm
                        self.requestDataLevel1()
                    dltLevel2 = nowTm - self.time_level2
                    if dltLevel2 >= self.wait_level2 and self.levelFlag2:
                        self.time_level2 = nowTm
                        self.requestDataLevel2()
                except er:
                    res = -1
                    break
        except er:
            print(er)
        return res
    def recPgk(self):
        res = 0 
        try:
            while self.isActived :
                recFlag , recBytes = self.readPgkData()
                if recFlag>0 :
                    self.logRec(recBytes)
                    if self.onRecDataEvent !=None and recFlag == 0x68:
                        try:
                            self.onRecDataEvent( recBytes )
                        except eer:
                            print(eer)

        except er:
            print(er)
        return res
    def logRec(self , dataBytes):
        log = 'rec -> '
        for i in dataBytes:
            log = log + ' ' + hex(i)
        print(log)
    def logSend(self , dataBytes):
        log = 'send-> '
        for i in dataBytes:
            log = log + ' ' + hex(i)
        print(log)
    def readPgkData(self ):
        res =0 
        resBytes = []
        try:
            serial = self.serial
            recHeaderBytes = serial.read(1)
            if len(recHeaderBytes)>0: 
                recHeader = recHeaderBytes[0]
                if recHeader==0xE5:
                    res = 0xE5
                elif recHeader==0x10:
                    time.sleep(0.1)
                    count = serial.inWaiting()
                    #print("rec body size :"+str(count))
                    if count>= 4 or 1>0:
                        recBytes = serial.read(4) 
                        if( len(recBytes) >=4 and recBytes[3]==0x16):
                            resBytes.append(0x10)
                            for i in recBytes:
                                resBytes.append(i)
                            res = 0x10
                elif recHeader == 0x68 :
                    res = 0x68
                    rec68HeadBytes = serial.read(3)
                    flag = len(rec68HeadBytes)>=3 and rec68HeadBytes[0]==rec68HeadBytes[1] and rec68HeadBytes[2]==0x68
                    if flag==True:
                        pgkSize = rec68HeadBytes[0]+2
                        rec68Bytes = serial.read(pgkSize)
                        if( len(rec68Bytes) >=pgkSize and rec68Bytes[pgkSize-1]==0x16):
                            resBytes.append(0x68)
                            for i in rec68Bytes:
                                resBytes.append(i)
                            
                            if rec68Bytes[0]==0x08 and  rec68Bytes[2]==0x64:
                                self.levelFlag1 = False
                                self.levelFlag2 = True

                            res = 0x68
                else:
                    res = -1 
        except er:
            print(er)
        return res , resBytes


class TPgk101:
    def __init__(self) -> None:
        self.dateBytes = None

        self.ctrlCode = 0
        self.ctrlCode_dir = 0
        self.ctrlCode_prm = 0
        self.ctrlCode_fcb = 0
        self.ctrlCode_fvc = 0
        self.ctrlCode_fun = 0
        
        self.linkAddr = 0
        self.asdu_type = 0 
        self.asdu_vsq = 0 
        self.asdu_vsq_sq = 0 
        self.asdu_vsq_num = 0 
        self.asdu_cot = 0 
        self.asdu_cot_t = 0 
        self.asdu_cot_pn = 0 
        self.asdu_cot_no = 0 
        self.asdu_addr = 0 
        self.asdu_data = None

        self.dataPoints =[]
    def decode( self , dataBytes ):
        try:

            #control code
            offset = 4
            self.ctrlCode = dataBytes[offset]
            self.decodeCtrlcode()
            offset = offset + 1
            #link address
            self.linkAddr = dataBytes[offset]
            offset = offset +1
            #asdu
            #asdu type
            self.asdu_type = dataBytes[offset]
            offset = offset + 1
            #asdu vsq
            self.asdu_vsq = dataBytes[offset]
            offset = offset + 1
            self.asdu_vsq_sq =self.asdu_vsq>>7
            self.asdu_vsq_num = (self.asdu_vsq<<1)>>1
            #asdu cot 
            self.asdu_cot = dataBytes[offset]
            offset = offset + 1
            self.asdu_cot_t = self.asdu_cot & 0x80
            self.asdu_cot_pn = self.asdu_cot & 0x40
            self.asdu_cot_no = self.asdu_cot & 0x3f
            #asdu address
            self.asdu_addr = dataBytes[offset]
            offset = offset + 1
            #asdt data 
            self.asdu_data = dataBytes[offset: len(dataBytes)-2]

            self.decodePoints()
        except er:
            print(er)
    def decodePoints( self  ):
        try:
            pointCount = self.asdu_vsq_num
            if pointCount>0:
                pointAddr  =0
                if self.asdu_vsq_sq == 0 :
                    pointAddr = int.from_bytes(self.asdu_data[0:2])
                    offset = 2
                    pointSize = int( (len(self.asdu_data) - 2 ) / self.asdu_vsq_num)
                    for i in range(pointCount):
                        dataPoint = TDataPoint()
                        dataPoint.pointAddr = pointAddr 
                        dataPoint.data = dataPoint[offset : offset + pointSize]
                        offset = offset + pointSize
                        self.dataPoints.append(dataPoint)
                else :
                    offset = 0
                    pointSize = int( (len(self.asdu_data) - 2 ) / self.asdu_vsq_num) - 2
                    for i in range(pointCount):
                        dataPoint = TDataPoint()
                        dataPoint.pointAddr = self.dataPoints[offset : offset + 2] 
                        offset = offset + 2
                        dataPoint.data = dataPoint[offset : offset + pointSize]
                        offset = offset + pointSize
                        self.dataPoints.append(dataPoint)
        except er:
            print(er)
class TDataPoint:
    def __init__(self) -> None:
        self.pointAddr =0 
        self.data = None
